/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package eu.xibit.jadardb;

import java.util.Iterator;
import java.util.TreeSet;

/**
 *
 * @author dipacs
 */
final class SpaceManager {

    static final class Space implements Comparable<Space> {

        private volatile long pointer;
        private volatile long size;

        public Space(long pointer, long size) {
            this.pointer = pointer;
            this.size = size;
        }

        public long getPointer() {
            return pointer;
        }

        public long getSize() {
            return size;
        }

        public void setPointer(long pointer) {
            this.pointer = pointer;
        }

        public void setSize(long size) {
            this.size = size;
        }

        @Override
        public int compareTo(Space o) {
            if (o == null) {
                return 1;
            }

            if (this.pointer < o.pointer) {
                return -1;
            } else if (this.pointer > o.pointer) {
                return 1;
            } else {
                return 0;
            }
        }

		@Override
		public int hashCode() {
			int hash = 3;
			hash = 79 * hash + (int) (this.pointer ^ (this.pointer >>> 32));
			return hash;
		}

		@Override
		public boolean equals(Object obj) {
			if (obj == null) {
				return false;
			}
			if (getClass() != obj.getClass()) {
				return false;
			}
			final Space other = (Space) obj;
			if (this.pointer != other.pointer) {
				return false;
			}
			return true;
		}
    }
    private final TreeSet<Space> reserved = new TreeSet<Space>();
    private final TreeSet<Space> free = new TreeSet<Space>();

    
    
    
    void delete(long pointer) {
        if (pointer < 0) {
            throw new IllegalArgumentException("Invalid pointer.");
        }
        
        
        Space actItem = reserved.ceiling(new Space(pointer, 1));
        if (actItem == null) {
            throw new IllegalArgumentException("Can't find data in the given pointer.");
        }
        if (actItem.getPointer() != pointer) {
            throw new IllegalArgumentException("Can't find data in the given pointer.");
        }
        reserved.remove(actItem);
        
        // extending free item after the removed item if needed
        Space freeItem = free.ceiling(new Space(actItem.getPointer() + actItem.getSize(), 1));
        if (freeItem != null && freeItem.getPointer() == actItem.getPointer() + actItem.getSize()) {
            freeItem.setPointer(actItem.getPointer());
            freeItem.setSize(freeItem.getSize() + actItem.getSize());
        } else {
            freeItem = null;
        }
        
        // extending free item before the removed item if needed
        Space freeItem2 = free.lower(new Space(actItem.getPointer(), 1));
        if (freeItem2 != null) {
            if (freeItem2.getPointer() + freeItem2.getSize() == actItem.getPointer()) {
                if (freeItem != null) {
                    freeItem.setPointer(freeItem2.getPointer());
                    freeItem.setSize(freeItem.getSize() + freeItem2.getSize());
                    free.remove(freeItem2);
                } else {
                    freeItem2.setSize(freeItem2.getSize() + actItem.getSize());
                }
            }
        }
    }

    long reserve(long size) {
        if (size < 1) {
            throw new IllegalArgumentException("Can't detect pointer for zero size.");
        }
        
        // TODO optimalizálni
        long pointer = -1;
        Iterator<Space> freeI = free.iterator();
        Space actItem;
        while (freeI.hasNext()) {
            actItem = freeI.next();
            if (actItem.getSize() == size) {
                pointer = actItem.getPointer();
                freeI.remove();
                break;
            } else if (actItem.getSize() > size) {
                pointer = actItem.getPointer();
                actItem.setPointer(actItem.getPointer() + size);
                actItem.setSize(actItem.getSize() - size);
                break;
            }
        }

        if (pointer < 0) {
            if (reserved.isEmpty()) {
                pointer = 0;
            } else {
                Space lastItem = reserved.last();
                pointer = lastItem.getPointer() + lastItem.getSize();
            }
        }

        reserved.add(new Space(pointer, size));

        return pointer;
    }

    void initSpace(long pointer, long size) {
        Space lastItem = null;
        if (!reserved.isEmpty()) {
            lastItem = reserved.last();
        }

        if (pointer == 0) {
            reserved.add(new Space(pointer, size));
        } else {
            if (lastItem == null) {
				reserved.add(new Space(pointer, size));
                free.add(new Space(0, pointer));
            } else {
                long freeSpace = pointer - (lastItem.getPointer() + lastItem.getSize());
                if (freeSpace < 0) {
                    throw new IllegalArgumentException("Illegal segment.");
                } else if (freeSpace > 0) {
					reserved.add(new Space(pointer, size));
                    free.add(new Space(lastItem.getPointer() + lastItem.getSize(), freeSpace));
                } else {
					reserved.add(new Space(pointer, size));
				}
            }
        }
    }
    
    long getNext(long pointer) {
        // empty set
        if (reserved.isEmpty()) {
            return -1;
        }
        
        // getting the higher element
        Space res = reserved.higher(new Space(pointer, 1));
        
        // check if there is a higher element
        if (res == null) {
            return -1;
        }
        
        // returning result
        return res.getPointer();
    }
    
    long getPrevious(long pointer) {
        // empty set
        if (reserved.isEmpty()) {
            return -1;
        }
        
        // getting the lower element
        Space res = reserved.lower(new Space(pointer, 1));
        
        // check if there is a lower element
        if (res == null) {
            return -1;
        }
        
        // returning result
        return res.getPointer();
    }
	
	long getNeededSize() {
		if (reserved.isEmpty()) {
			return 0;
		}
		
		Space last = reserved.last();
		return last.getPointer() + last.getSize();
	}
	
	void clear() {
		reserved.clear();
		free.clear();
	}
	
	Space getFirstSpace() {
		if (free.isEmpty()) {
			return null;
		}
		
		return free.first();
	}
    
}
